<?php


namespace App\Service;


use App\Model\Device;
use App\Utils\Logger;
use Carbon\Carbon;
use App\Model\Normal;
use Hyperf\Utils\ApplicationContext;
use Psr\Container\ContainerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;

class DeviceMqttService
{
    /**
     * @Inject
     * @var ContainerInterface
     */
    protected $container;
    const CRC8_TABLE = [
        0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
        0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
        0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
        0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
        0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
        0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
        0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
        0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
        0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
        0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
        0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
        0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
        0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
        0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
        0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
        0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
    ];
    protected $callbackMqttService;

    protected Redis $redis;

    public function __construct(CallbackMqttService $callbackMqttService)
    {
        $this->callbackMqttService = $callbackMqttService;
        $this->redis = ApplicationContext::getContainer()->get(Redis::class);
    }


    public function subscribe($message, $topic)
    {
        try{

            if (substr($message, 0, 1) === '+') {
            //    Normal::logger('主题数据格式错误'.$message.'------'.$topic, 'order-queue');
                return '';
            }

            $mac = substr($message, 0, 12);

            $id = substr($message, 12, 2);
            switch ($id) {
                case '54':
                    // 心跳
                    $this->heartbeat($message, $topic);
                    $tick = substr($message, 14, 4);
                    $cmd = $mac . "54" . "00" . $tick;
                    $dataBytes = hex2bin($cmd);
                    $crc8 = $this->crc8Calculate($dataBytes);
                    // 格式化校验码
                    $crcHex = sprintf('%02X', $crc8);
                    $cmd.=$crcHex;
                    return $this->returnData($cmd);
                //电量设置
                case '45':
                    $this->elecCallback($message);
                    break;
                //状态查询
                case '51':
                    $this->statusQuery($message);
                    break;
                //开关闸
                case '57':
                    $this->statusCallback($message);
                    break;
                //重启设置
                case '52':
                    $this->restartCallback($message);
                    break;
                //设置参数
                case '55':
                    $this->setParams($message);
                    break;
                //设置mqtt
                case '43':
                    $this->setMqtt($message);
                    break;
            }
        }catch (\Exception $e){
            Normal::logger($message.'------'.$topic.'------'.$e->getMessage().$e->getTraceAsString(), 'order-queue');
        }
        //var_dump($mac, $id);
    }

    public function crc8Calculate($dataBytes) {
        $crc = 0;
        for ($i = 0; $i < strlen($dataBytes); $i++) {
            $byte = ord($dataBytes[$i]); // 假设输入是 ASCII 字符
            $crc ^= $byte;
            $crc = self::CRC8_TABLE[$crc];
        }
        return $crc;
    }

    public function setMqtt($message){

    }

    public function setParams($message){

        $data['path'] = 'setParams';
        $data['mac'] = substr($message, 0, 12);
        $data['id'] = substr($message, 12, 2);
        $data['sap'] =  hexdec(substr($message, 14, 2));
        $device = Device::where('mac', $data['mac'])->with('user')->first();
        $opt = hexdec(substr($message, 16, 2));
        $data['value'] = hexdec(substr($message, 20, 8));
        switch($opt){
            case 0x02:
            case 0x82:
                $data['option'] ='speed';
                $device->speed = $data['value'];
                break;
            case 0x81:
            case 0x01:
                $data['option'] = 'mode';
                $device->pay_mode = $data['value'];
        }
        $device->save();
        if($opt != 0xFE){
            if($device->user){
                $data['api_url'] = $device->user->api_url;
                $this->callbackMqttService->publish($data);
            }
            $this->callbackMqttService->publish($data);
        }
    }


    # 心跳
    public function heartbeat($message, $topic)
    {
        $data['path'] = 'heart';

        $data['mac'] = substr($message, 0, 12);

        $data['id'] = substr($message, 12, 2);

        // 主表
        # 心跳编号，回复需带上
        $data['tick'] = hexdec(substr($message, 14, 4));

        $iccid = (string)substr($message, 18, 40);
        $data['iccid'] = hex2bin($iccid);
        # 4G网络信息 20-33 强，10-20 中，10以下差, 99无信号
        $data['signal'] = hexdec(substr($message, 58, 2));
        # 累计电量
        $hexString = substr($message, 60, 8);
        // 将十六进制字符串转换为无符号整数
        $unsignedInt = hexdec($hexString);
        // 如果最高位为1，表示负数，需要进行符号位处理
        // 首先确定最高位的值（这里假设最高位在十六进制的最左边）
         $mask = 0x80000000;
        // 使用位与运算符检查最高位
        if ($unsignedInt &$mask) {
            // 最高位为1，表示有符号整数中的负数
            // 转换为有符号整数
            $data['total'] =$unsignedInt - 0x100000000;
        } else {
            // 最高位为0，表示正数
            $data['total'] =$unsignedInt;
        }

        # 剩余电量
        $hexString1 = substr($message, 68, 8);
        // 将十六进制字符串转换为无符号整数
        $unsignedInt1 = hexdec($hexString1);
        // 如果最高位为1，表示负数，需要进行符号位处理
        // 首先确定最高位的值（这里假设最高位在十六进制的最左边）
        $mask = 0x80000000;
        // 使用位与运算符检查最高位
        if ($unsignedInt1 &$mask) {
            // 最高位为1，表示有符号整数中的负数
            // 转换为有符号整数
            $data['du'] =$unsignedInt1 - 0x100000000;
        } else {
            // 最高位为0，表示正数
            $data['du'] =$unsignedInt1;
        }

        # 总功率
        $data['w'] = hexdec(substr($message, 76, 8));

        #电压
        $data['volt'] = hexdec(substr($message, 84, 8));

        #电流
        $data['curr'] =  hexdec(substr($message, 92, 8));

        # 状态
        $data['status'] = hexdec(substr($message, 100, 2));
        // 运行状态 0正常，1过载
        $data['emsStatus'] = hexdec(substr($message, 102, 2));

        $device = Device::where('mac', $data['mac'])->with('user')->first();
//        $devicePowerLogService = $this->container->get(DevicePowerLogService::class);
        if ($device) {
            $data['is_master'] = $device->is_master;
            $data['sn'] = $device->sn;
//            $xh = ($data['total'] > 0 ? $data['total'] / 1000 : 0) - $device->total;
            $device->signal = $data['signal'];
            $device->du = $data['du'] != 0 ? $data['du'] / 1000 : 0;
            $device->total = $data['total'] > 0 ? $data['total'] / 1000 : 0;
            $device->status = $data['status'];
            $device->power = $data['w'] > 0 ? $data['w'] / 1000 : 0;
            $device->netType = 1;
            $device->clientId = $topic;
            $device->readAt = Carbon::now()->toDateTimeString();
            $device->onlineStatus = 1;
            $device->emsStatus = $data['emsStatus'];
            $device->iccid = $data['iccid'];
            $device->volt = $data['volt']?$data['volt']/1000:0;
            $device->curr = $data['curr']?$data['curr']/1000:0;
            $device->save();
            if($device->user){
                // 添加默认值处理
                $heartCycle = $device->heart_hycle > 45 ? $device->heart_hycle : 45;
                if ($heartCycle > 45) {
                    $cacheKey = 'device:heart:cache:' . $device->id;
                    if (!$this->redis->exists($cacheKey)) {
                        $data['api_url'] = $device->user->api_url;
                        $this->callbackMqttService->publish($data);
                        $this->redis->setex($cacheKey, $heartCycle, 1);
                    }
                   // Normal::logger($data['id'].'heartbeat:'.$data['mac'],'order-queue');
                } else {
                    $data['api_url'] = $device->user->api_url;
                    $this->callbackMqttService->publish($data);
                }
            }
            # 电表日志
//            $devicePowerLogService->update($device->id, $du, $total, $w, $xh,$signal);
        } else {
            Logger::get()->error($data['mac'].'device not found', [$data['mac']]);
        }

    }

    # 状态查询
    public function statusQuery($message)
    {
        $data['path'] = 'statusQuery';
        $data['mac'] = substr($message, 0, 12);
        $data['id'] = substr($message, 12, 2);
//        Normal::logger($data['id'].'status-query:'.$data['mac'],'order-queue');
        $data['sap'] = hexdec(substr($message, 14, 2));
        $data['acku'] = hexdec(substr($message, 16, 2));
        if ($data['acku'] != '00') {
            return;
        }
        # 累计电量
        $hexString = substr($message, 18, 8);
        // 将十六进制字符串转换为无符号整数
        $unsignedInt = hexdec($hexString);
        // 如果最高位为1，表示负数，需要进行符号位处理
        // 首先确定最高位的值（这里假设最高位在十六进制的最左边）
        $mask = 0x80000000;
        // 使用位与运算符检查最高位
        if ($unsignedInt &$mask) {
            // 最高位为1，表示有符号整数中的负数
            // 转换为有符号整数
            $data['total'] =$unsignedInt - 0x100000000;
        } else {
            // 最高位为0，表示正数
            $data['total'] =$unsignedInt;
        }

        #剩余电量
        $hexString1 = substr($message, 26, 8);
        // 将十六进制字符串转换为无符号整数
        $unsignedInt = hexdec($hexString1);
        // 如果最高位为1，表示负数，需要进行符号位处理
        // 首先确定最高位的值（这里假设最高位在十六进制的最左边）
        $mask = 0x80000000;
        // 使用位与运算符检查最高位
        if ($unsignedInt &$mask) {
            // 最高位为1，表示有符号整数中的负数
            // 转换为有符号整数
            $data['du'] =$unsignedInt - 0x100000000;
        } else {
            // 最高位为0，表示正数
            $data['du'] =$unsignedInt;
        }


        # 总功率
        $data['w']= hexdec(substr($message, 34, 8));
        #电压
        $data['volt'] = hexdec(substr($message, 42, 8));
        #电流
        $data['curr'] =  hexdec(substr($message, 50, 8));
        # 状态
        $data['status'] = hexdec(substr($message, 58, 2));
        // 运行状态 0正常，1过载
        $data['emsStatus'] = hexdec(substr($message, 60, 2));

        $device = Device::where('mac', $data['mac'])->with('user')->first();
        if ($device) {
            $device->du = $data['du'] != 0 ? $data['du'] / 1000 : 0;
            $device->total = $data['total'] > 0 ? $data['total'] / 1000 : 0;
            $device->status = $data['status'];
            $device->power = $data['w'] > 0 ? $data['w'] / 1000 : 0;
            $device->readAt = Carbon::now()->toDateTimeString();
            $device->onlineStatus = 1;
            $device->emsStatus = $data['emsStatus'];
            $device->volt = $data['volt'];
            $device->curr = $data['curr'];
            $device->save();
            if($device->user){
                $data['api_url'] = $device->user->api_url;
                $this->callbackMqttService->publish($data);
            }
        }
    }

    # 设置电量回调
    public function elecCallback($message)
    {
        $data['path'] = 'elecCallback';
        $data['mac'] = substr($message, 0, 12);
        $data['id'] = substr($message, 12, 2);
        $data['acku'] = hexdec(substr($message,16,2));
        $data['sap'] = hexdec(substr($message,14,2));
        # 剩余电量
        $data['du'] = substr($message, 18, 8);
        $unsignedInt = hexdec($data['du']);
        // 如果最高位为1，表示负数，需要进行符号位处理
        // 首先确定最高位的值（这里假设最高位在十六进制的最左边）
         $mask = 0x80000000;
        // 使用位与运算符检查最高位
        if ($unsignedInt &$mask) {
            // 最高位为1，表示有符号整数中的负数
            // 转换为有符号整数
            $data['du']=  $du =$unsignedInt - 0x100000000;
        } else {
            // 最高位为0，表示正数
            $data['du']= $du =$unsignedInt;
        }
        # log
        $device = Device::where('mac', $data['mac'])->with('user')->first();
        if ($device) {
            $device->du = $data['du'] != 0 ? $data['du'] / 1000 : 0;
            //$device->readAt = Carbon::now()->toDateTimeString();
            $device->recharge_callback_at = Carbon::now()->toDateTimeString();
            $device->save();
            if($device->user){
                $data['api_url'] = $device->user->api_url;
                $this->callbackMqttService->publish($data);
            }
        }

    }

     # 重启回调
    public function restartCallback($message)
    {
        // +00098A9AEA016B430D0002D5
        $data['path'] = 'restartCallback';
        $data['mac'] = substr($message, 0, 12);
        $data['id'] = substr($message, 12, 2);
        # 状态
        $data['sap']= hexdec(substr($message, 14, 2));
        $data['acku']= hexdec(substr($message, 16, 2));
        $device = Device::where('mac', $data['mac'])->with('user')->first();
        if ($device) {
            $device->save();
            if($device->user){
                $data['api_url'] = $device->user->api_url;
                $this->callbackMqttService->publish($data);
            }
        }
    }

    # 拉合闸回调
    public function statusCallback($message)
    {
        // +00098A9AEA016B430D0002D5
        $data['path'] = 'statusCallback';
        $data['mac'] = substr($message, 0, 12);
        $data['id'] = substr($message, 12, 2);
        $data['sap'] = hexdec(substr($message,14,2));
        # 状态
        $data['status'] = hexdec(substr($message, 16, 2));
        $device = Device::where('mac', $data['mac'])->with('user')->first();
        if ($device) {
            $device->status = $data['status'];
            $device->readAt = Carbon::now()->toDateTimeString();
            $device->save();
            if($device->user){
                $data['api_url'] = $device->user->api_url;
                $this->callbackMqttService->publish($data);
            }
        }
    }


    public function returnData($content): string
    {
        return $this->toLen( strtoupper( dechex( ( strlen($content) / 2 ) ) ) ) . $content . '00';
    }

        public function returnData1($content): string
    {
        return ":" . $this->toLen( strtoupper( dechex( ( strlen($content) / 2 ) ) ) ) . $content . "\r\n";
    }

    public function toLen($l, $t = 4)
    {
        $len = strlen($l);
        if (strlen($l) < $t) {
            for ($i = 0; $i < ($t - $len); $i ++) {
                $l = '0' . $l;
            }
        }
        return $l;
    }

    public function intTo4ByteSigned($n) {
        $n = (int)$n;
        // 限制数值在 32 位有符号整数范围内
        $n = $n & 0xFFFFFFFF;
        if ($n >= 0x80000000) {
            $n -= 0x100000000; // 处理负数（二进制补码）
        }

        // 转换为大端（Big-Endian）字节序列
        $bytes = [
            sprintf("%02X", ($n >> 24) & 0xFF),
            sprintf("%02X", ($n >> 16) & 0xFF),
            sprintf("%02X", ($n >> 8) & 0xFF),
            sprintf("%02X", $n & 0xFF)
        ];
        $res = '';
        foreach($bytes as $v){
            $res.=$v;
        }
        return $res;
    }

    public function reverseMac($mac)
    {
        $newMac = substr($mac, 10, 2) . substr($mac, 8, 2) .
            substr($mac, 6, 2). substr($mac, 4, 2) . substr($mac, 2, 2) .
            substr($mac, 0, 2);
        Logger::get()->notice('new mac', [$newMac]);
        return $newMac;
    }
}
